/**
 * Collaborative Story Development Web Application (CSDApp) 
 * Copyright Anton Strack 2014
 *
 * This file is part of Collaborative Story Development Application (CSDApp).
 *
 * CSDApp is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * CSDApp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * CSDApp. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package csdwa;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * This class holds a particular value that needs to be validated against. The
 * value is added to this object and then the list of validators that the value
 * is required to pass in order to be considered valid. As this object stores
 * the value itself, it can then be used by the later code to access the value
 * according to it's true type. This class already contains the IsNotNull
 * validator to ensure that there are no null pointer exceptions and thus
 * IsNotNull doesn't need to be added by client code.
 *
 * @author Anton Strack
 */
public class ValidatorValue {

    /**
     * The value of type Object that will be validated.
     */
    private Object value;
    /**
     * The name of the value that will be validated. This is primarily used for
     * error messages.
     */
    private String name;
    /**
     * A list of validators that the value must be validated against to be
     * considered true when calling "isValid()"
     */
    private List<CsdwaValidator> validators;
    /**
     * The first index value of the validator in the validators list that
     * returns false. It defaults to -1 indicating that there are not errors
     * detected;
     */
    private int errorIndex = -1;
    /**
     * This value holds the validation result from the last "isValid()" call.
     * This value is intended to reduce multiple re-validations that would
     * always return the same result.
     */
    private boolean valid;

    public ValidatorValue() {
        this.validators = new ArrayList<CsdwaValidator>();
        this.validators.add(new IsNotNull());// default validator to ensure no null values.
    }

    public ValidatorValue(Object value, String name) {
        this();
        this.value = value;
        this.name = name;
    }

    public ValidatorValue(String value, String name) {
        this();
        this.value = value;
        this.name = name;
    }

    /**
     * This method preforms the validation checks on the value using the list of
     * validators. It returns the result of the validation and also sets the
     * valid variable which can be accessed using isValidResult.
     *
     * @return true if the value passes all validation checks from the list of
     * added validators and false if any validator returns false.
     */
    public boolean isValid() {
        this.valid = true;
        if (this.getValidators().isEmpty() == false) {
            int i = 0;
            int count = this.validators.size();
            while (this.valid == true && i < count) {
                this.valid = this.validators.get(i).isValid(value);
                i++;
            }
            if (this.valid == false) {
                this.errorIndex = i - 1;
            }
        } else {
            this.valid = false;
        }
        return this.valid;
    }

    /**
     * A list of validators that the value must be validated against to be
     * considered true when calling "isValidResult()"
     *
     * @param validator the validator to set
     * @return this ValidatorValue object to allow for chaining the method
     * calls.
     */
    public ValidatorValue addValidator(CsdwaValidator validator) {
        this.getValidators().add(validator);
        return this;// allow chaining of addValidator methods
    }

    /**
     * Get the error message that indicates the value failed to isValid.
     *
     * @return the error message of the validator the value failed to to pass.
     * If there were no errors then return an empty string.
     */
    public String getErrorMessage() {
        if (this.getValidators().isEmpty() == false) {
            if (this.getErrorIndex() > -1) {
                return this.getValidators().get(getErrorIndex()).getErrorMessage(this.name);
            } else {
                return "";
            }
        }
        return "There are no validators set for the validatorValue:" + this.getName();
    }

    /**
     * The value of type Object that will be validated.
     *
     * @return the value
     */
    public Object getValue() {
        return value;
    }

    /**
     * The value of type String that will be validated.
     *
     * @return the value
     */
    public String getString() {
        return (String) value;
    }

    /**
     * The value of type Number that will be validated.
     *
     * @return the value
     */
    public Integer getInteger() {
        if (this.value instanceof java.lang.String) {
            return Integer.parseInt((String) value);
        }
        return (Integer) value;
    }

    /**
     * This method geths the value as a String List. It checks that the value is
     * an instance of List but it can only assume the List is of Strings.
     *
     * @return Returns the list of Strings or an empty List of Strings if the
     * value isn't really a list.
     */
    public List<String> getList() {
        List<String> list;
        if (this.value instanceof java.util.List) {
            list = (List<String>) value;
        } else {
            list = new ArrayList<String>();
        }
        return list;
    }

    /**
     * The value of type Object that will be validated.
     *
     * @param value the value to set
     */
    public void setValue(Object value) {
        this.value = value;
    }

    /**
     * The value of type String that will be validated.
     *
     * @param value the value to set
     */
    public void setValue(String value) {
        this.value = value;
    }

    /**
     * The value of type Number that will be validated.
     *
     * @param value the value to set
     */
    public void setValue(Number value) {
        this.value = value;
    }

    /**
     * The name of the value that will be validated. This is primarily used for
     * error messages.
     *
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * The name of the value that will be validated. This is primarily used for
     * error messages.
     *
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * A list of validators that the value must be validated against to be
     * considered true when calling "isValid()"
     *
     * @return the validators
     */
    public List<CsdwaValidator> getValidators() {
        return validators;
    }

    /**
     * The first index value of the validator in the validators list that
     * returns false.
     *
     * @return the errorIndex
     */
    public int getErrorIndex() {
        return errorIndex;
    }

    /**
     * The first index value of the validator in the validators list that
     * returns false.
     *
     * @param errorIndex the errorIndex to set
     */
    protected void setErrorIndex(int errorIndex) {
        this.errorIndex = errorIndex;
    }

    /**
     * This value holds the validation result from the last "isValid()" call.
     * This value is intended to reduce multiple re-validations that would
     * always return the same result.
     *
     * @return the true/false result of the last isValid() call.
     */
    public boolean isValidResult() {
        return valid;
    }

    /**
     * This value holds the validation result from the last "isValidResult()"
     * call. This value is intended to reduce multiple re-validations that would
     * always return the same result.
     *
     * @param valid the valid to set
     */
    public void setValid(boolean valid) {
        this.valid = valid;
    }
}
